package cn.achao.csr.openapi.service.impl;

import cn.achao.csr.api.model.entity.RestBean;
import cn.achao.csr.api.model.entity.SysApi;
import cn.achao.csr.api.model.enums.ThirdServiceType;
import cn.achao.csr.openapi.core.annotation.ThirdServiceReq;
import cn.achao.csr.openapi.core.annotation.ThirdServiceResp;
import cn.achao.csr.openapi.core.model.req.ThirdRequest;
import cn.achao.csr.openapi.core.model.resp.ThirdResponse;
import cn.achao.csr.openapi.core.utils.HttpResUtils;
import cn.achao.csr.openapi.service.ThirdService;
import cn.achao.csr.openapi.service.cache.SysApiCacheService;
import cn.hutool.core.bean.BeanUtil;
import cn.hutool.core.util.XmlUtil;
import cn.hutool.json.JSONUtil;
import cn.hutool.json.XML;
import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONObject;
import com.alibaba.fastjson.serializer.SerializerFeature;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.springframework.http.HttpMethod;
import org.springframework.stereotype.Service;
import org.w3c.dom.Element;

import java.util.Map;
import java.util.concurrent.atomic.AtomicReference;

/**
 * @Author: achao
 **/
@Slf4j
@Service
@RequiredArgsConstructor
public class ThirdServiceImpl<Req extends ThirdRequest, Resp extends ThirdResponse> implements ThirdService<Req, Resp> {

    private final SysApiCacheService sysApiCacheService;

    @Override
    public Resp sendHttp(Req req, Class<Req> reqClass, Class<Resp> respClass) {

        // 预处理
        // 参数合法性校验
        if (!reqClass.isAnnotationPresent(ThirdServiceReq.class)) {
            return ThirdResponse.fail(respClass, "请求必须使用ThirdServiceReq注解");
        }

        ThirdServiceReq thirdServiceReq = reqClass.getAnnotation(ThirdServiceReq.class);

        // 请求的服务编码非空校验
        if (StringUtils.isEmpty(thirdServiceReq.serviceCode())) {
            return ThirdResponse.fail(respClass, "请求的服务编码不能为空");
        }

        // 请求的服务存在性校验
        SysApi api = sysApiCacheService.getApi(thirdServiceReq.serviceCode());
        if (api == null) {
            return ThirdResponse.fail(respClass, "请求的服务不存在");
        }

        // 响应合法化校验
        if (!respClass.isAnnotationPresent(ThirdServiceResp.class)) {
            return ThirdResponse.fail(respClass, "响应必须使用ThirdServiceResp注解");
        }

        // 请求预处理
        if (!req.before()) {
            return ThirdResponse.fail(respClass, "请求预处理失败");
        }
        // 发送请求
        Resp resp = ThirdResponse.fail(respClass, "请求失败");

        // 发送HTTP GET请求
        if (ThirdServiceType.HTTP_GET.ordinal() == api.getRequestType()) {
            AtomicReference<String> stringAtomicReference = new AtomicReference<>("");
            BeanUtil.beanToMap(req).forEach((k, v) -> {
                if (v != null && StringUtils.isNotBlank((String) v)) {
                    stringAtomicReference.set(stringAtomicReference + "&" + k + "=" + v);
                }
            });
            String uri = stringAtomicReference.get();
            if (uri.startsWith("&")) {
                uri = uri.substring(1);
                uri = "?" + uri;
            }
            String address = api.getAddress() + uri;
            api.setAddress(address);

            resp = sendGet(respClass, api);
        }
        // 发送HTTP GET RESTFUL请求
        if (ThirdServiceType.HTTP_GET_RESTFUL.ordinal() == api.getRequestType()) {
            // 构建请求地址
            StringBuilder urlBuilder = new StringBuilder(api.getAddress());
            Map<String, Object> paramMap = BeanUtil.beanToMap(req);
            if (!paramMap.isEmpty()) {
                urlBuilder.append("/");
                for (Map.Entry<String, Object> entry : paramMap.entrySet()) {
                    urlBuilder.append(entry.getValue()).append("/");
                }
                urlBuilder.deleteCharAt(urlBuilder.length() - 1);
            }
            api.setAddress(urlBuilder.toString());
            resp = sendGet(respClass, api);
        }
        // 发送HTTP POST(application/json)请求
        if (ThirdServiceType.HTTP_POST_JSON.ordinal() == api.getRequestType()) {
            resp = sendPostJson(req, respClass, api);
        }
        // TODO 发送HTTP POST()请求
        if (ThirdServiceType.HTTP_POST_FORM.ordinal() == api.getRequestType()) {
            resp = sendPostJson(req, respClass, api);
        }
        // TODO 发送HTTP POST(application/xml)请求
        if (ThirdServiceType.HTTP_POST_XML.ordinal() == api.getRequestType()) {
            resp = sendPostXML(req, respClass, api);
        }

        // 响应后置处理
        if (!resp.after()) {
            return ThirdResponse.fail(respClass, "响应后置处理失败");
        }
        return resp;

    }


    private Resp sendPostJson(Req req, Class<Resp> respClass, SysApi api) {
        String json = JSON.toJSONStringWithDateFormat(req, "yyyy-MM-dd HH:mm:ss", SerializerFeature.DisableCircularReferenceDetect);
        RestBean restBean = HttpResUtils.post(HttpMethod.POST, api, json);

        // 200响应校验
        if (restBean.getCode() != 200) {
            return ThirdResponse.fail(respClass, "接口请求失败");
        }

        // HTTP+JSON响应报文格式化
        String respJson = restBean.getResp();
        Resp resp = JSONObject.parseObject(respJson, respClass);
        log.info("ThirdSend POST响应:{}", resp);
        return resp;
    }

    private Resp sendPostXML(Req req, Class<Resp> respClass, SysApi api) {
        String xml = XML.toXml(JSONUtil.parseObj(JSON.toJSON(req)));
        RestBean restBean = HttpResUtils.post(HttpMethod.POST, api, xml);
        // 200响应校验
        if (restBean.getCode() != 200) {
            return ThirdResponse.fail(respClass, "接口请求失败");
        }
        String xmlResp = restBean.getResp();
        // 将xml转换为java对象
        Element rootElement = XmlUtil.getRootElement(XmlUtil.parseXml(xmlResp));
        // 使用根节点下的直接 转成对象
        Resp resp = XmlUtil.xmlToBean(rootElement, respClass);
        log.info("ThirdSend POST_XML响应:{}", resp);
        return resp;
    }

    private Resp sendGet(Class<Resp> respClass, SysApi api) {
        // 构建并发送HTTP GET请求
        RestBean restBean = HttpResUtils.get(HttpMethod.GET, api);
        // 200响应校验
        if (restBean.getCode() != 200) {
            return ThirdResponse.fail(respClass, "接口请求失败");
        }

        // HTTP GET XML响应报文格式化
        Resp resp = JSONObject.parseObject(restBean.getResp(), respClass);
        log.info("ThirdSend GET响应:{}", resp);
        return resp;
    }
}

